package me.zhengjie.server.impl;

import me.zhengjie.dao.ContainerDao;
import me.zhengjie.dao.MaterialDao;
import me.zhengjie.dao.StoreLocalDao;
import me.zhengjie.enums.ConstantStatus;
import me.zhengjie.enums.InboundStatusEnum;
import me.zhengjie.juc.StoreLock;
import me.zhengjie.network.MesNetworkRequest;
import me.zhengjie.server.*;
import me.zhengjie.vo.Inbound.MesMaterialVo;
import me.zhengjie.vo.mes.ResponseResult;
import me.zhengjie.vo.Result;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class InboundServiceImpl implements InboundService {

    @Resource
    private MaterialService materialService;


    @Resource
    private StoreLocalService storeLocalService;

    @Resource
    private ContainerService containerService;

    @Resource
    private OutboundOrderService outboundOrderService;


    /**
     * 入库储位申请
     *
     * @param mesMaterialVo
     * @return
     */
    @Override
    public Result<String> InboundStorageApply(MesMaterialVo mesMaterialVo) {

        String containerCode = mesMaterialVo.getContainerCode();
        if (containerCode == null || containerCode.isEmpty()) {
            return Result.error("请输入载具编码");
        }
        ContainerDao containerDao = containerService.findDataOne(containerCode);
        if (containerDao == null || containerDao.getId() == null) {
            return Result.error("载具编码不在载具表管理中");
        }
        Integer ionLock = containerDao.getIonLock();
        if (Objects.equals(ionLock, ConstantStatus.CONTAINER_LOCKED)) {
            return Result.error("载具已被锁定");
        }
        // 是否回库
        Boolean isNest = mesMaterialVo.getNest();

        List<MaterialDao> materialVoData = mesMaterialVo.getData();

        Result<String> result = new Result<>();
        // 如果没有物料信息，则分配空箱储位
        if ((isNest == null || !isNest) && (materialVoData == null || materialVoData.isEmpty())) {
            return this.emptyContainerInbound(mesMaterialVo, containerCode);
        }
        // 记录物料条码信息
        List<String> itemBarCodeList = new ArrayList<>();
        for (MaterialDao datum : materialVoData) {
            itemBarCodeList.add(datum.getItemBarCode());
        }
        // 入库流程，物料不存在物料表中
        if (Boolean.FALSE.equals(isNest)) {
            List<MaterialDao> codeList = materialService.findDataList(itemBarCodeList);
            if (codeList != null && !codeList.isEmpty()) {
                return Result.error("物料已存在");
            }
        }
        // 回库与正常入库
        String goodType = null;
        if (Boolean.TRUE.equals(isNest)) {
            // 根据载具编码获取要物料信息
            materialVoData = materialService
                    .findDataDao(new MaterialDao().setContainerCode(containerCode));
            goodType = this.isContainerCodeNest(materialVoData);
        }
        return this.normalInbound(materialVoData, mesMaterialVo, itemBarCodeList, goodType, isNest, result);
    }

    /**
     * 获取物料信息
     *
     * @param mesMaterialVo //
     * @return
     */

    public Result<String> emptyContainerInbound(MesMaterialVo mesMaterialVo, String containerCode) {
        boolean isLockAcquired;
        Result<String> result;
        try {
            String floor = mesMaterialVo.getFloor();
            isLockAcquired = StoreLock.getReentrantInLock().tryLock(5, TimeUnit.MINUTES);
            if (!isLockAcquired) {
                return Result.error("未获取到锁");
            }
            String storeId = storeLocalService.findStoreByType(null, floor, null);
            // 更新储位状态
            StoreLocalDao storeLocalDao = new StoreLocalDao()
                    .setId(storeId)
                    .setStatus(InboundStatusEnum.INBOUND_LOCK.status);
            storeLocalService.updateData(storeLocalDao);

            // 载具锁定
            ContainerDao containerDao = new ContainerDao().setId(containerCode).setIonLock(ConstantStatus.CONTAINER_LOCKED);
            containerService.updateDataById(containerDao);
            result = Result.success(storeId);
        } catch (Exception e) {
            result = Result.error("储位分配异常");
        } finally {
            StoreLock.getReentrantInLock().unlock();
        }
        return result;
    }

    public Result<String> normalInbound(List<MaterialDao> materialVoData, MesMaterialVo mesMaterialVo, List<String> itemBarCodeList,
                                        String goodType, Boolean isNest, Result<String> result) {
        boolean isLockAcquired;
        try {
            String floor = mesMaterialVo.getFloor();
            String containerCode = mesMaterialVo.getContainerCode();
            isLockAcquired = StoreLock.getReentrantInLock().tryLock(5, TimeUnit.MINUTES);
            if (!isLockAcquired) {
                return Result.error("未获取到锁");
            }
            MaterialDao materialDao = getMaterialDaoByList(materialVoData);
            // 分配储位
            String storeId = storeLocalService.findStoreByType(materialDao, floor, goodType);
            if (storeId == null || storeId.isEmpty()) {
                return Result.error("未获取到储位");
            }


            // 更新储位状态
            StoreLocalDao storeLocalDao = new StoreLocalDao()
                    .setId(storeId)
                    .setStatus(InboundStatusEnum.INBOUND_LOCK.status);
            storeLocalService.updateData(storeLocalDao);

            // 载具锁定
            ContainerDao containerDao = new ContainerDao().setId(containerCode).setIonLock(ConstantStatus.CONTAINER_LOCKED);
            containerService.updateDataById(containerDao);
            result = Result.success(storeId);
            if (isNest != null && isNest) {
                MaterialDao material = new MaterialDao().setContainerCode(containerCode);
                materialService.updateDataByList(itemBarCodeList, material);
            } else {
                materialService.saveBatch(materialVoData);
            }
        } catch (Exception e) {
            result = Result.error(e.getMessage());
        } finally {
            StoreLock.getReentrantInLock().unlock();
        }
        return result;
    }

    public String isContainerCodeNest(List<MaterialDao> materialVoData) {
        Set<String> materilaCaodeSet = new HashSet<>();
        for (MaterialDao datum : materialVoData) {
            materilaCaodeSet.add(datum.getMaterialCode());
        }
        // 调用出库任务接口，判断是否有需要出库物料在正在回库中
        List<String> getOutInboundTask = outboundOrderService.exitOutboudOrder();
        for (String itemBarCode : getOutInboundTask) {
            if (materilaCaodeSet.contains(itemBarCode)) {
                // 如果存在，把该物料存入理货区靠近出入口区域
                return ConstantStatus.OPERATION_AREA;
            }
        }
        return null;
    }


    /**
     * 判断库存是否存在已在架的容器载具
     *
     * @param request 请求参数
     * @return true:存在 false:不存在
     */
    @Override
    public Result<Boolean> findContainerCodeIsExist(Map<String, Object> request) {
        if (request == null) {
            return new Result<>(201, "请求参数为空", null);
        }
        if (request.get("containerCode") == null || request.get("nest") == null) {
            return new Result<>(201, "请求参数为空", null);
        }
        Boolean nest = (Boolean) request.get("nest");
        if (nest) {
            return new Result<>(200, "回库流程无需判断是否存在相同载具！", true);
        }
        String containerCode = (String) request.get("containerCode");
        // 判断容器是否存在
        if (containerService.findDataOne(containerCode) == null){
            ContainerDao containerDao = new ContainerDao()
                    .setId(containerCode)
                    .setType("")
                    .setLocal(ConstantStatus.OutsideTheWarehouse)
                    .setDisable(ConstantStatus.NORMAL)
                    .setIsExist(ConstantStatus.Out_OF_Stock)
                    .setBarCodeBad(0)
                    .setContainerBad(0)
                    .setCreateDate(new Timestamp(System.currentTimeMillis()));
            containerService.save(containerDao);
        }
        // 判断储位是否有该载具
        StoreLocalDao storeLocalDao = storeLocalService
                .findData(new StoreLocalDao().setContainerCode(containerCode));
        if (storeLocalDao != null) {
            return new Result<>(200, "储位存在相同容器", true);
        }
        return new Result<>(200, "不存在相同容器", false);
    }

    /**
     * 撤销储位申请
     *
     * @param request 请求参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<?> revokeStorageApply(Map<String, Object> request) {

        if (request == null || !request.containsKey("storageId") || !request.containsKey("containerCode") || !request.containsKey("nest")) {
            return Result.error("请求参数为空");
        }

        String storeId = (String) request.get("storageId");
        String containerCode = (String) request.get("containerCode");
        Boolean isNest = (Boolean) request.get("nest");


        StoreLocalDao storeById = storeLocalService
                .findData(new StoreLocalDao().setId(storeId));
        if (storeById == null) {
            return Result.error("储位不存在");
        }
        if (!storeById.getStatus().equals(InboundStatusEnum.INBOUND_LOCK.status)) {
            return Result.error("储位状态不为锁定");
        }
        if (!isNest) {
            materialService.deleteByContainerCode(containerCode);
        }
        // 更新储位状态
        StoreLocalDao storeLocalDao = new StoreLocalDao()
                .setId(storeId)
                .setStatus(InboundStatusEnum.STORAGE_FREE.status);
        boolean result = storeLocalService.updateData(storeLocalDao);
        if (!result) {
            return Result.error("撤销储位状态失败");
        }
        return Result.success("撤销储位状态失败");
    }

    /**
     * @param materialVoData 中间表物料信息
     * @return 物料信息最多的厂商和物料类型
     */
    public MaterialDao getMaterialDaoByList(List<MaterialDao> materialVoData) {
        Map<String, Integer> map = new TreeMap<>();
        for (MaterialDao materialDao : materialVoData) {
            String materialCategory = materialDao.getMaterialCategory();
            if (map.containsKey(materialCategory)) {
                map.put(materialCategory, map.get(materialCategory) + 1);
            } else {
                map.put(materialCategory, 1);
            }
        }
        String materialCategory = "";
        int maxCount = -1;
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            if (entry.getValue() > maxCount) {
                // 比较当前元素的出现次数与最大值
                materialCategory = entry.getKey();
                maxCount = entry.getValue();
            }
        }
        for (MaterialDao materialVoDatum : materialVoData) {
            if (materialVoDatum.getMaterialCategory().equals(materialCategory)) {
                return materialVoDatum;
            }
        }
        return null;
    }

    /**
     * @param materialDao     物料信息
     * @param materialVoDatum 中间表物料信息
     * @return true:物料信息一致 false:物料信息不一致
     */
    public boolean getMaterialCategory(MaterialDao materialDao, MaterialDao materialVoDatum) {
        return materialDao.getMaterialCategory().equals(materialVoDatum.getMaterialCategory())
                && materialDao.getManufacturers().equals(materialVoDatum.getManufacturers());
    }

    /**
     * 发送信息给MES系统
     *
     * @param materialVoData 中间表物料信息
     * @param containerCode  容器编号
     */
    public void sendMesNest(List<MaterialDao> materialVoData, String containerCode) {
        // 回库嵌套
        ResponseResult result = MesNetworkRequest.nestedReturnToLibraryRequest(materialVoData, containerCode);
    }

    /**
     * 入库
     *
     * @param containerCode  容器编号
     * @param oldWarehouse   原仓库
     * @param oldStorageCode 原储位
     * @param newWarehouse   新仓库
     * @param storeCode      新储位
     * @return 入库结果
     */
    public void sendMesInbound(String containerCode,
                               String storeCode,
                               String oldStorageCode,
                               String oldWarehouse,
                               String newWarehouse) {
        // 发送MES接口
        ResponseResult result = MesNetworkRequest.InventorySynchronizationRequest(containerCode, oldStorageCode, oldWarehouse,
                storeCode, newWarehouse);
    }
}
